Canigó - Servei de Configuració
SERVEI DE CONFIGURACIÓ
IntroduccióPropósitEl Servei de Configuració té com a propósit la configuració de les propietats de qualsevol component de l'aplicació. Aquestes propietats poden ser tant referències a altres objectes com propietats internes (atributs) que necessiten per al seu correcte funcionament. Aquesta configuració es pot realitzar de 2 formes diferenciades:
Una de les formes tradicionals d'estructurar el nostre codi és que siguin els components qui realitzin la localització i instanciació dels serveis o components que han de cridar dins la seva lógica (així com l'obtenció de les propietats internes) En aquesta aproximació, encara utilitzada però poc pràctica, es pot fer ús del patró factory per amagar la complexitat de la instanciació i obtenció de les instàncies requerides. Tot i això, aquest mecanisme l'han de realitzar els clients, els quals han de conéixer quines dependències tenen els components entre sí i per tant conéixer quina seqüència han de seguir per la inicialització de cadascun d'ells. En altres casos, l'especificació J2EE fa ús de JNDI com a mecanisme d'obtenció de referències d'objectes. Aquesta implementació requereix molts canvis si es fa un canvi d'entorn i deixa d'usar-se JNDI.
Mitjançant l'ús del patró Dependency Injection el nostre aplicatiu només ha de definir les dependències, i deixar que un codi extern (el framework) s'encarregui de la complexitat d'instanciació, inicialització i seqüència de les referència que requereixin els nostres components. Aquest patró elimina totes les problemàtiques de la forma tradicional. canigo es basa en l'ús del patró 'Dependency Injection' mitjançant l'ús de Spring. El més important és que el seu ús no és intrusiu, ja que en qualsevol moment podríem canviar de mecanisme intern sense afectar el nostre codi. Context i Escenaris d'ÚsEl Servei de Configuració es troba dins dels serveis de Propósit General de canigo. Versions i DependènciesLes dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte: A qui va dirigitEs recomana una lectura prèvia de http://static.springframework.org/spring/docs/1.2.x/reference/beans.html Documents i Fonts de ReferènciaPer a començar a fer ús del Servei de Configuració és imprescindible la lectura dels següents documents:
I es recomana la lectura de les següents referències:
GlossariDependency Injection Patró de disseny que estableix un nivell d'abstracció mitjançant la definició de interfícies i eliminant la dependència entre components mitjançant un codi extern que injecta aquestes dependències de forma transparent. Existeixen 3 formes del patró: 'setter', 'constructor' i 'interface based injection'. Descripció DetalladaArquitectura i ComponentsEls components podem classificar-los en:
Es pot trobar tota la documentació JavaDoc y el codi font referent aquests components a les següents urls: JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-configuration/apidocs/index.html Instal- lació i ConfiguracióInstal- lacióLa instal- lació del servei requereix de la utilització de la llibreria 'canigo-services-configuration' i les dependències indicades a l'apartat 'Introducció-Versions i Dependències'. ConfiguracióLa configuració del Servei de Configuració es realitza principalment amb el mecanisme d'injecció utilitzat. En el cas de Spring consultar http://static.springframework.org/spring/docs/1.2.x/reference/beans.html. A més de la configuració ja existent mitjançant el mecanisme d'injecció base (Spring) s'ofereixen les següents facilitats:
En cap lloc s'especificarà quin és el servei de configuració que es fa servir per l'obtenció dels components i la seva injecció. Spring, per defecte cercarà totes les configuracions que s'hagin definit amb les classes configuradores (PropertyPlaceHolderConfigurer,...). Configuració segons màquina Atributs:
Propietats:
Propietats:
Exemple:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="configurationService"class="net.gencat.ctti.canigo.services. configuration.springframework.beans.factory.config.HostPropertyPlaceholderConfigurer"> <property name="basePropertyFiles"> <list> <value>classpath:jdbc/jdbc.properties</value> </list> </property> </bean> </beans> En el cas a dalt mostrat, si el DNS de la màquina és 'ES-57PYM1J' es cercarà el fitxer de configuració 'jdbc/jdbc.properties.ES-57PYM1J'. En cas que no es trobi aquest fitxer es cercaria el fitxer ' jdbc/jdbc.properties'.
Configuració de variables del sistema Ús de múltiples fitxers de configuracióSovint és útil dividir el conjunt de definicions en múltiples fitxers XML. Mitjançant alguns dels configuradors és possible referenciar més d'un fitxer. Tot i això és possible que volguem repartir els fitxers en diferents parts. En aquest cas podem fer ús de l'element 'import'.
<beans> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/> <bean id="bean2" class="..."/> . . . En aquest exemple, les definicions externes de beans s'estan carregant des de 3 fitxers, services.xml, messageSource.xml i themeSource.xml. Tots els paths són considerats relatius al fitxer de definició que realitza la importació, per tant en aquest cas services.xml ha d'estar en el mateix directori o classpath location que el fitxer que realitza la importació, mentres que messageSource.xml i themeSource.xml han d'estar en el directori resources per sota del fitxer que realitza la importació. Es pot comprovar que una barra inicial és ignorada, però atés que aquests paths són relatius, probablement és millor no usar la barra. Els continguts dels fitxers importats han de ser definicions XML de beans completament vàlides d'acord amb el DTD, incloent l'element bean de més alt nivell. ![]() Utilització del ServeiDegut a que la configuració dels elements de l'aplicació o dels serveis es realitza de forma externa mitjançant el patró 'Dependency Injection' no és convenient l'obtenció de propietats des dels clients. Aquesta obtenció es realitza de forma transparent fora de les classes de l'aplicació. Obtenció de valors de configuració des de les classesSi en algun cas es requereix de l'obtenció de propietats externes des del nostre codi (opció no recomanada), podem fer ús de la interfície 'ConfigurationService'.
package net.gencat.ctti.canigo.services.configuration; ... public interface ConfigurationService { public String getProperty(String key) throws ConfigurationServiceException; } Es proporciona una implementació basada en Spring, anomenada 'HostPropertyPlaceholderConfigurer' El mètode que s'exposa és un i es correspon a la següent signatura:
Si el valor de configuració no es troba, es llença una excepció de tipus 'ConfigurationServiceException' indicant que el valor que es busca no s'ha trobat. Per a utilitzar-lo, només cal que afegim al fitxer XML de definició de beans el nostre servei de configuració i injectem als nostres beans el servei de configuració, tal i com es mostra a continuació:
... <bean id="myBean" class="com.myapp.model.MyBean"> <property name="configService" ref="configurationService" /> </bean> ... <bean id="configurationService" class="net.gencat.ctti.canigo. services.configuration.springframework.beans. factory.config.HostPropertyResourceConfigurer."> <property name="basePropertyFiles"> <list> <value>classpath:config.properties</value> </list> </property> </bean> ...
I des de la classe que vol fer ús del servei:
... public class MyBean { private ConfigurationService configService = null; public String myMethod() throws Exception { String configValue = configService.getProperty("config.value");* ... } } Integració amb Altres ServeisEl Servei de Configuració és usat per tots els Serveis per tal de definir les seves propietats de forma externa. En comptats casos, algunes de les implementacions sobre el que es troben els serveis de canigo no permeten la definició de les seves propietats mitjançant la injecció. Servei de Traces basat en Log4JEn el Servei de Traces canigo ofereix la possibilitat de configurar diferents fitxers segons el host en el que es troba l'aplicació mitjançant la classe 'net.gencat.ctti.canigo.services.logging.log4j.xml.HostDOMConfigurator'. Per a més referència consultar el document 'Servei de Traces'. ExemplesExemple de ConfiguracióImaginem que tenim un DAO que fa ús d'una factoria de sessions per llençar els objectes de negoci contra una capa de persistència i persistir les dades. Aquesta relació es representa en la següent figura: ![]() Aquests elements i relacions es representen amb un fitxer XML amb una estructura definida. En el nostre exemple, tindríem un fitxer XML amb la següent informació:
A continuació es mostra aquest fitxer:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!--Element estructural que representa la factoria de sessions--> <bean name="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> </bean> <!--Element estructural que representa el DAO--> <bean id="myDao" class="test.dao.hibernate.impl.HibernateCategoryDAOImpl"> <!--Relació entre el nostre DAO i la factoria de sessions--> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans> Tal i com defineix Spring, cada 'bean' representa un element o actor del nostre aplicatiu i es representa al XML com un node <bean/>. Els valors més importants a configurar en aquest node són:
Per a una explicació en més detall dels diferents atributs del node <bean/> consultar la url: http://static.springframework.org/spring/docs/1.2.x/reference/beans.html. A part els atributs del node <bean/>, aquest pot tenir subnodes, que principalment representen altres 'beans' que necessita per a treballar, és a dir, els col.laboradors. En el nostre exemple hem definit un node <property/> amb el nom 'sessionFactory' que fa referència al col.laborador: el 'bean' amb nom 'sessionFactory' que representa la factoria de sessions.. El nom de les <property/> es correspon amb els noms de les variables d'instància del propi 'bean' La definició d'aquests fitxers de configuració XML servirà posteriorment per anar a buscar en l'aplicatiu, en el cas que sigui necessari, les instàncies dels beans corresponents. Hi haurà doncs un procès automàtic que llegirà aquests fitxers i farà de punt d'entrada per a qualsevol petició de 'bean'. Aquest procès l'executa una entitat que es coneix com a 'BeanFactory'. L'existència d'aquesta factory en una aplicació J2EE bé representada per una classe que es diu 'WebApplicationContext'. ??* Nota: L'existència d'aquesta factory és totalment transparent al desenvolupador, ja que no s'ha de preocuperar de conèixer, a no ser que sigui necessari, de l'existència de la mateixa, si no de la correcta configuració dels XML per a que aquesta resolgui les dependències entre col.laboradors correctament. Exemple de Configuració amb Definició segons l'EntornEn l'anterior exemple hem vist la forma de configurar la nostra aplicació en funció dels fitxers XML que representen els nostres 'beans' i les seves relacions. Però hi han aspectes que s'escapen en aquesta configuració:
Definició de Propietats segons EntornImaginem que en l'escenari que hem presentat a la Figura 1 i afegim un 'dataSourceBean' que fa de factoria de connexions i que té una relació de col.laboració amb la factoria de sessions: ![]() Al fitxer de configuració abans presentat s'afegeix el següent bean: ... <bean id="dataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="$\{jdbc.driverClassName\}" /> <property name="url" value="$\{jdbc.url\}" /> <property name="username" value="$\{jdbc.username\}" /> <property name="password" value="$\{jdbc.password\}" /> </bean> ... i es modifica la configuració del bean 'sessionFactory' per afegir la relació de col.laboració:
<property name="dataSource" ref="dataSourceBean" /> Si ens fixem en les propietats del bean 'dataSourceBean' veurem que hi han quatre <property/> sensibles de canviar segons l'entorn:
Els valors d'aquests atributs s'extreuen d'un fitxer extern segons la definició '${jdbc.driverClassName},${jdbc.url},${jdbc.username} i ${jdbc.password}. Però, d'ón es treuen els valors d'aquestes variables? En aquest punt intervé el servei de configuració: mitjançant les classes 'HostPropertyPlaceholderConfigurer' i 'HostPropertyResourceConfigurer' que trobem en el package 'net.gencat.ctti.canigo.services.configuration.springframework.beans.factory.config' es configura quina és la localització del fitxer extern.
... <bean id="dataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="$\{jdbc.driverClassName\}" /> <property name="url" value="$\{jdbc.url\}" /> <property name="username" value="$\{jdbc.username\}" /> <property name="password" value="$\{jdbc.password\}" /> </bean>... <bean id="configurationService" class="net.gencat.ctti.canigo.services.configuration. springframework.beans.factory. config.HostPropertyPlaceholderConfigurer"> <list> <value>classpath:config.propertiesc</value> </list> </bean> ... Configuració del fitxer extern
jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb: D:/path_to_db/testDB jdbc.username=sa jdbc.password= |